/*
 * filemanager.cpp
 *
 *      Author: Jerome RD Soine and Christoph A Brand
 *      Institution: Schwarz goup, Institute for Theoretical Physics, Heidelberg University, Germany
 */

#include "../../../../../../include/base/cell_models/ACM/geometry/util/filemanager.h"
#include "../../../../../../include/base/cell_models/ACM/geometry/geomobject.h"
#include "../../../../../../include/base/cell_models/ACM/geometry/base/vertex.h"
#include "../../../../../../include/base/cell_models/ACM/geometry/base/edge.h"
#include "../../../../../../include/base/cell_models/ACM/geometry/base/triangle.h"
#include "../../../../../../include/base/cell_models/ACM/ActiveCableModel.h"

#include <boost/algorithm/string.hpp>

#include <fstream>
#include <sstream>
#include <list>
#include <string>
#include <algorithm>
#include <iostream>

using namespace std;

FileManager::FileManager(GeomObject * G, string SNAPSHOT_PATH, bool clear) {
	g = G;
	numberOfSavedStates = 0;
	snapshotPath = SNAPSHOT_PATH;
	cde = NULL;
	fpe = NULL;
	if(clear) clearSnapshotDirectory();
}

void FileManager::clearSnapshotDirectory(){
	ostringstream os;
	os << "rm " << snapshotPath << "/*";
	system(os.str().c_str());
}

void FileManager::load(string filename) {
	ifstream fin(filename.c_str());
	loadVTK(fin);
	fin.close();
}

void FileManager::loadVTK(ifstream & fin) {
	string line;
	while (getline(fin, line)) {
		istringstream iline(line);
		string firstword;
		getline(iline, firstword, ' ');
		firstword = boost::to_upper_copy(firstword);
		if (firstword == "POINTS") {
			string numberString;
			getline(iline, numberString, ' ');
			int numberofpoints = atoi(numberString.c_str());
			initializeVerticesFromVTK(fin, numberofpoints);
		} else if (firstword == "CELLS") {
			string numberString;
			getline(iline, numberString, ' ');
			int numberofcells = atoi(numberString.c_str());
			initializeCellsFromVTK(fin, numberofcells);
		} else if(firstword == "CELL_DATA") {
			string numberString;
			getline(iline, numberString, ' ');
			int numberofcells = atoi(numberString.c_str());

			getline(fin, line);
			istringstream iss(line);
			string type, title;
			getline(iss, type, ' ');
			getline(iss, title, ' ');

			//get rid of lookup_table line
			getline(fin, line);

			if(type == "SCALARS" && title == "stress_fibre_type") initializeStressFibresFromVTK(fin, numberofcells);
		} else if(firstword == "POINT_DATA") {
			string numberString;
			getline(iline, numberString, ' ');
			int numberofpoints = atoi(numberString.c_str());

			getline(fin, line);
			istringstream iss(line);
			string type, title;
			getline(iss, type, ' ');
			getline(iss, title, ' ');

			//get rid of lookup_table line
			getline(fin, line);

			if(type == "SCALARS" && title == "points_fixed") initializeFixedPointsFromVTK(fin, numberofpoints);
		} else {
			//skip
		}
	}
}

void FileManager::initializeStressFibresFromVTK(ifstream & fin, int numberofcells){
	vector<Edge*> edges = *g->getEdges();
	for (int i = 0; i < numberofcells; i++) {
		string typestring;
		getline(fin, typestring);
		istringstream stream(typestring);
		string s_type, s_number;
		stream >> s_type;
		stream >> s_number;
		int type = atoi(s_type.c_str());
		int number = atoi(s_number.c_str());
		//cout << type << " - " << number << endl;
		edges[i]->putNumericalAttribute(ActiveCableModel::FIELD_FIBRETYPE, (double) type);
		edges[i]->putNumericalAttribute(ActiveCableModel::FIELD_FIBRENUMBER, (double) number);
	}
}

void FileManager::initializeFixedPointsFromVTK(ifstream & fin, int numberofpoints){
	vector<Vertex*> vertices = *g->getVertices();
	for (int i = 0; i < numberofpoints; i++) {
		string typestring;
		getline(fin, typestring);
		int type = atoi(typestring.c_str());
		if(type==1) vertices[i]->setFixed(true);
	}
}

void FileManager::initializeVerticesFromVTK(ifstream & fin, int numberofvertices) {
	vector<Vertex*> vertices;
	vertices.reserve(numberofvertices);
	for (int i = 0; i < numberofvertices; i++) {
		string pointstring;
		getline(fin, pointstring);
		vertices.push_back( createVertexFromVTKString(pointstring) );
	}
	g->setVertices(vertices);
}

void FileManager::initializeCellsFromVTK(ifstream & fin, int numberofcells) {
	vector<Edge*> edgeList;
	vector<Triangle*> triangleList;
	vector<vector<Vertex*> > tipList;

	vector<Vertex*>* vertices = g->getVertices();

	for (int i = 0; i < numberofcells; i++) {
		string cellstring;
		getline(fin, cellstring);
		istringstream icell(cellstring);
		string numberOfTips;
		getline(icell, numberOfTips, ' ');
		if (numberOfTips == "3") {
			//Cell is triangle
			vector<Vertex*> tips = read3TipsFromVTK(icell, vertices);
			Triangle * t = createTriangle(tips, edgeList, true);
			triangleList.push_back(t);
		} else if(numberOfTips == "2") {
			//Cell is line
			vector<Vertex*> tips = read2TipsFromVTK(icell, vertices);
			getOrCreateEdgeConnectingVertices(tips[0], tips[1],edgeList);
		}else{
			//Cell is something else
		}
	}

	initializeEdges(edgeList);
	initializeTriangles(triangleList);
}

vector<Vertex*> FileManager::read2TipsFromVTK(istringstream & icell,
		vector<Vertex*>* & vertices){
	string tip1String, tip2String;
	getline(icell, tip1String, ' ');
	getline(icell, tip2String, ' ');
	vector<Vertex*> tips;
	tips.push_back((*vertices)[atoi(tip1String.c_str())]);
	tips.push_back((*vertices)[atoi(tip2String.c_str())]);
	return tips;
}


vector<Vertex*> FileManager::read3TipsFromVTK(istringstream & icell,
		vector<Vertex*>* & vertices){
	string tip1String, tip2String, tip3String;
	getline(icell, tip1String, ' ');
	getline(icell, tip2String, ' ');
	getline(icell, tip3String, ' ');
	vector<Vertex*> tips;
	tips.push_back((*vertices)[atoi(tip1String.c_str())]);
	tips.push_back((*vertices)[atoi(tip2String.c_str())]);
	tips.push_back((*vertices)[atoi(tip3String.c_str())]);
	return tips;
}

Triangle* FileManager::createTriangle(vector<Vertex*> tips, vector<Edge*> & edgeList, bool firstTriangle) {
		Edge* e12 = getOrCreateEdgeConnectingVertices(tips[0], tips[1], edgeList);
		Edge* e23 = getOrCreateEdgeConnectingVertices(tips[1], tips[2], edgeList);
		Edge* e31 = getOrCreateEdgeConnectingVertices(tips[2], tips[0], edgeList);
		return new Triangle(tips[0], tips[1], tips[2], e12, e23, e31);
}

void FileManager::initializeEdges(vector<Edge*> & edgeList) {
	vector<Edge*>* edges = g->getEdges();
	edges->reserve(edgeList.size());
	for (unsigned int i=0; i<edgeList.size();i++) {
		edges->push_back( edgeList[i]);
	}
	edgeList.clear();
}

void FileManager::initializeTriangles(vector<Triangle*> & triangleList) {
	vector<Triangle*>* triangles = g->getTriangles();
	triangles->reserve(triangleList.size());
	for (unsigned int i=0; i<triangleList.size();i++) {
		triangles->push_back( triangleList[i]);
	}
	triangleList.clear();
}

Vertex* FileManager::createVertexFromVTKString(string vertex) {
	istringstream ipoint(vertex);
	string xString, yString, zString;
	getline(ipoint, xString, ' ');
	getline(ipoint, yString, ' ');
	getline(ipoint, zString, ' ');
	double x = atof(xString.c_str());
	double y = atof(yString.c_str());
	double z = atof(zString.c_str());

	return new Vertex(x, y, z);
}

Edge* FileManager::getOrCreateEdgeConnectingVertices(Vertex* v1, Vertex* v2,
		vector<Edge*> & edgelist) {

	//search for edge
	for (unsigned int i=0; i<edgelist.size(); i++) {
		bool foundEdge = edgelist[i]->connectsVertices(v1, v2);
		if (foundEdge) {
			return edgelist[i];
		}
	}

	//No edge connects Vertices, create new one
	Edge *newEdge = new Edge(v2,v1);
	edgelist.push_back(newEdge);
	return newEdge;
}



void FileManager::save(string filename) {
	ofstream fout(filename.c_str());
	saveVTK(fout);
	fout << "\n";
	fout.flush();
	fout.close();
}

void FileManager::saveLines(string filename){
	ofstream fout(filename.c_str());
	saveLineVTK(fout);
	fout.close();
}

void FileManager::saveLineVTK(ofstream & fout) {
	printVTKHeader(fout);
	printVTKPoints(fout);
	printVTKLines(fout);
}

void FileManager::saveVTK(ofstream & fout) {
	printVTKHeader(fout);
	printVTKPoints(fout);
	printVTKCells(fout);
}

void FileManager::printVTKHeader(ofstream & fout) {
	fout << "# vtk DataFile Version 2.0" << endl;
	fout << "TITLE" << endl;
	fout << "ASCII" << endl;
	fout << "DATASET UNSTRUCTURED_GRID" << endl;
}

void FileManager::printVTKPoints(ofstream & fout) {
	vector<Vertex*> vertices = *(g->getVertices());

	fout << "POINTS " << vertices.size() << " float" << endl;
	int nr = 0;
	for (unsigned int i = 0; i < vertices.size(); i++) {
		Vertex * currentVertex = vertices[i];
		fout << (*currentVertex).toVTKString() << endl;
		currentVertex->setIndex(nr++);
	}
	fout << endl;
}

void FileManager::printVTKCells(ofstream & fout) {
	vector<Triangle*> triangles = *g->getTriangles();
	int numberOfTriangles = triangles.size();
	vector<Edge*> edges = *g->getEdges();
	int numberOfEdges = edges.size();

	int length_of_cell_list = numberOfTriangles
			* number_of_points_in_a_triangle_data_line + numberOfEdges * number_of_points_in_a_line_data_line;
	int number_of_cells = numberOfTriangles + numberOfEdges;

	fout << "CELLS " << number_of_cells << " " << length_of_cell_list << endl;

	if(numberOfEdges > 0)
		printVTKLines(fout);
	if(numberOfTriangles > 0)
		printVTKTriangles(fout);

	fout << "CELL_TYPES " << number_of_cells << endl;
	for (int i = 0; i < numberOfEdges; i++) {
		fout << cell_type_line << endl;
	}
	for (int i = 0; i < numberOfTriangles; i++) {
		fout << cell_type_triangle << endl;
	}

	if(cde != NULL) {
		fout << "CELL_DATA " << number_of_cells << endl;
		cde->write(fout);
	}
	if(fpe != NULL){
		fout << "POINT_DATA " << g->getVertices()->size() << endl;
		fpe->writeToFile(fout);
	}
}

void FileManager::printVTKTriangles(ofstream & fout){
	vector<Triangle*> triangles = *g->getTriangles();
	int numberOfTriangles = triangles.size();
	for (int i = 0; i < numberOfTriangles; i++) {
		printVTKTriangle(fout, triangles[i]);
	}
}

void FileManager::printVTKLines(ofstream & fout) {
	vector<Edge*> edges = *g->getEdges();
	for (unsigned int i = 0; i < edges.size(); i++) {
		printVTKLine(fout, edges[i]);
	}
	fout << endl;
}

void FileManager::printVTKTriangle(ofstream & fout, Triangle * t) {
	fout << number_of_tips_of_a_triangle << " ";
	vector<Vertex*> tips = *(t->getTips());
	for (int i = 0; i < number_of_tips_of_a_triangle; i++) {
		fout << tips[i]->getIndex() << " ";
	}
	fout << endl;
}

void FileManager::printVTKLine(ofstream & fout, Edge * e) {
	fout << number_of_tips_of_a_line << " ";
	fout << e->getHead()->getIndex() << " ";
	fout << e->getTail()->getIndex() << " ";
	fout << endl;
}


void FileManager::saveSnapshot() {
	ostringstream os;
	os << snapshotPath << "/snapshot" << numberOfSavedStates++ << ".vtk";
	save(os.str());
}

